/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.dwh.trigger;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.nio.channels.IllegalBlockingModeException;
import java.util.ArrayList;
import java.util.HashMap;
import static org.dwh.trigger.Conf.*;
import static org.dwh.trigger.Log.*;

/**
 *
 * @author Jose Carlos Palma
 */
public class Server extends Thread {

    private String name;
    private ServerSocket server;
    /**
     * Indica si el hilo se esta o no ejecutando.
     */
    private boolean running = false;
    /**
     * Indica que el hilo termino su ejecucion.
     */
    private boolean shutdown = true;
    /**
     * Lista de SLUs.
     */
    private ArrayList<SLUReader> slus = new ArrayList<SLUReader>();
    /**
     * Lista de informacio de las SLUs.
     */
    private ArrayList<SLUInfo> slusInfo;
    /**
     * Lista de Colas.
     */
    private ArrayList<QueueWriter> queues = new ArrayList<QueueWriter>();
    /**
     * Lista de informacion de las Colas.
     */
    private ArrayList<QueueInfo> queuesInfo;
    /**
     * HashMap de los tipo de trigger y la cola para ese tipo.
     */
    public static HashMap<String, QueueWriter> hashQueues = new HashMap<String, QueueWriter>();

    /**
     *
     */
    public Server() {
        try {
            this.name = get("server.name");
            setName("Server - " + name);
            this.server = new ServerSocket(getInt("server.port", 60001));
            this.server.setSoTimeout(getInt("server.timeout", 60000));
        } catch (IOException ex) {
            error("Server: " + name + " [IOException] can't to created server, "
                    + (ex.getStackTrace() != null ? ex.getStackTrace()[ex.getStackTrace().length - 1] : ex.getMessage()));
            Runtime.getRuntime().exit(1);
        }
        //Carga la informacion de las SLUs
        slusInfo = getSLUsInfo();
        //Carga la informacion de las colas
        queuesInfo = getQueuesInfo();
    }

    /**
     *
     */
    private void crearColas() {
        for (QueueInfo queueInfo : queuesInfo) {
            QueueWriter w = new QueueWriter(queueInfo);
            //Agregar la cola a lista de colas
            queues.add(w);

            for (String t : queueInfo.getTypes()) {
                if (t != null && !t.isEmpty()) {
                    hashQueues.put(t, w);
                }
            }

            //Inicia la cola para su escritura
            w.start();
        }



        //Espera a que todas las colas esten conectadas
        info("Server: " + name + " is hoping that all queues are connected...");
        while (running) {
            boolean flag = true;
            for (QueueWriter q : queues) {
                if (q.getInfo().isOn() && !q.isRunning()) {
                    flag = false;
                }
            }
            if (flag) {
                break;
            }
            pause(2000);
        }
        info("Server: " + name + ", all queues are connected...");

    }

    /**
     * Hace una pausa en milisegundos.
     *
     * @param millis tiempo en milisegundo.
     */
    public void pause(long millis) {
        try {
            sleep(millis);
        } catch (InterruptedException ex) {
        }
    }

    /**
     *
     * @param sluIp
     * @return
     */
    private SLUInfo getSLUInfo(String sluIp) {
        SLUInfo retVal = null;
        for (SLUInfo sluInfo : slusInfo) {
            if (sluIp.equals(sluInfo.getIp())) {
                retVal = sluInfo;
                break;
            }
        }
        return retVal;
    }

    /**
     *
     * @param queueName
     * @return
     */
    private QueueInfo getQueueInfo(String queueName) {
        QueueInfo retVal = null;
        for (QueueInfo queueInfo : queuesInfo) {
            if (queueName.equals(queueInfo.getName())) {
                retVal = queueInfo;
                break;
            }
        }
        return retVal;
    }

    /**
     *
     */
    public void turnOff() {
        this.running = false;
        try {
            this.server.close();
        } catch (IOException ex) {
        }

    }

    /**
     *
     * @return
     */
    public boolean isShutdown() {
        return this.shutdown;
    }

    /**
     *
     */
    @Override
    public void run() {
        info("Server: " + name + " is starting...");

        this.shutdown = false;
        this.running = true;
        info("Server: " + name + " is running...");

        crearColas();


        while (running && !server.isClosed()) {
            try {
                //Espera una conexion con SLU
                Socket slu = server.accept();
                String ip = slu.getInetAddress().getHostAddress();
                SLUInfo sluInfo = getSLUInfo(ip);

                if (sluInfo == null) {
                    warning("Server: " + name + " rejected to SLU " + ip);
                    try {
                        slu.close();
                    } catch (IOException ex) {
                    }
                    continue;
                }
                SLUReader sr = new SLUReader(slu, sluInfo);
                slus.add(sr);
                accept("Server: " + name + " accepted to SLU " + sluInfo);
                sr.start();
            } catch (SocketTimeoutException ex) {
                warning("Server: " + name + " [SocketTimeoutException] can't to connect with SLU, "
                        + (ex.getStackTrace() != null ? ex.getStackTrace()[ex.getStackTrace().length - 1] : ex.getMessage()));
            } catch (SecurityException ex) {
                error("Server: " + name + " [SecurityException] can't to connect with SLU, "
                        + (ex.getStackTrace() != null ? ex.getStackTrace()[ex.getStackTrace().length - 1] : ex.getMessage()));
                break;
            } catch (IllegalBlockingModeException ex) {
                error("Server: " + name + " [IllegalBlockingModeException] can't to connect with SLU, "
                        + (ex.getStackTrace() != null ? ex.getStackTrace()[ex.getStackTrace().length - 1] : ex.getMessage()));
                break;
            } catch (IOException ex) {
                error("Server: " + name + " [IOException] can't to connect with SLU, "
                        + (ex.getStackTrace() != null ? ex.getStackTrace()[ex.getStackTrace().length - 1] : ex.getMessage()));
                break;
            }
        }


        info("Server: " + name + " is shuting down...");

        for (SLUReader sr : slus) {
            if (sr.getInfo().isOff() || sr.isShutdown()) {
                continue;
            }
            info("Server: " + name + " is closing to " + sr.getInfo());
            sr.turnOff();
            for (int i = 0; i < 50 && !sr.isShutdown(); i++) {
                pause(200);
            }
        }

        for (QueueWriter qw : queues) {
            if (qw.getInfo().isOff() || qw.isShutdown()) {
                continue;
            }
            info("Server: " + name + " is closing to " + qw.getInfo());
            qw.turnOff();
            for (int i = 0; i < 50 && !qw.isShutdown(); i++) {
                pause(200);
            }
        }

        try {
            server.close();
        } catch (IOException ex) {
        }

        this.shutdown = true;

        info("Server: " + name + " is shutdown");


    }
}
